/* Copyright (c) 2008 Charles Rich and Worcester Polytechnic Institute.
 * All Rights Reserved.  Use is subject to license terms.  See the file
 * "license.terms" for information on usage and redistribution of this
 * file and for a DISCLAIMER OF ALL WARRANTIES.
 */
package edu.wpi.cetask;

import org.w3c.dom.*;

import java.util.*;

public class TaskClass extends TaskModel.Member {
   
   // TODO check for duplicate slot names
   
   TaskClass (Node node, TaskModel model) { model.super(node); } 
  
   public Task newInstance () { return new Task(this, engine); }
   
   /**
    * Test whether this task class is primitive (relative to models in current
    * task recognizer). A task class is primitive iff there are no known
    * decomposition types with this task as goal.
    */
   public boolean isPrimitive () {
      return getDecompositions().isEmpty();
   }
   
   /**
    * Return list of input slot names, including "external".  Note "device"
    * has predefined meaning, but is not automatically an input to every task.
    * 
    * @see #getDeclaredInputNames()
    */
   public List<String> getInputNames () {
      List<String> inputs = getDeclaredInputNames();
      inputs.add("external"); 
      return inputs;
   }
   
   /**
    * Return list of output slot names, including "success" and "when".
    * 
    * @see #getDeclaredOutputNames()
    */
   public List<String> getOutputNames () { 
      List<String> inputs = getDeclaredOutputNames();
      inputs.add("success");
      inputs.add("when");
      return inputs;
   }
   
   /**
    * Return list of declared input slot names.
    * 
    * @see #getInputNames()
    */
   public List<String> getDeclaredInputNames () {
      return xpathValues("./input/@name");
   }
   
   /**
    * Return list of declared output slot names.
    * 
    * @see #getInputNames()
    */
   public List<String> getDeclaredOutputNames () { 
      return xpathValues("./output/@name");
   }

   public String getSlotType (String name) {
      if ( name.equals("success") || name.equals("external") )
         return "boolean";
      else if ( name.equals("when") ) return "Date";
      String path = "[@name=\""+name+"\"]/@type";
      String type = xpath("./input"+path+" | "+"./output"+path);
      if ( type.length() == 0 ) 
         throw new IllegalArgumentException(name+" is not slot of "+this);
      return type;
   }
   
   public List<DecompositionClass> getDecompositions () {
      return getDescriptions("subtasks");
   }
   
   /**
    * Return decomposition class with given id for this task class or null if none.
    */
   public DecompositionClass getDecomposition (String id) {
      for (DecompositionClass decomp : getDecompositions())
         if ( decomp.getId().equals(id) ) return decomp;
      return null;
   }
   
   public List<Script> getScripts () {
      // cannot return init scripts, since schema doesn't allow task with init
      return getDescriptions("script");
   }

   private <T extends Description.Taskable> 
             List<T> getDescriptions (String element) {
      List<T> descriptions = new ArrayList<T>();
      String path = "./"+element;
      List<Node> nodes = engine.xpathAll(path);
      for (Node node : nodes) {
         Description.Taskable description = newTaskable(element, node, null);
         if ( equals(description.getTask()) ) descriptions.add((T) description);
      }
      // add nested forms 
      for (Node node : xpathNodes(path)) 
         descriptions.add((T) newTaskable(element, node, this));
      return descriptions;
   }
}

